package com.dcy.gateway.config;

import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.dcy.gateway.sentinel.JsonSentinelGatewayBlockExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.result.view.ViewResolver;

import javax.annotation.PostConstruct;
import java.util.*;

/**
 * @Author：dcy
 * @Description: sentinel限流配置
 * @Date: 2021/4/22 8:31
 * 启动时加入 JVM 参数 -Dcsp.sentinel.dashboard.server=consoleIp:port 指定控制台地址和端口。
 * 若启动多个应用，则需要通过 -Dcsp.sentinel.api.port=xxxx 指定客户端监控 API 的端口（默认是 8719）。
 * vm参数：
 * -Dproject.name=gateway-center -Dcsp.sentinel.dashboard.server=控制台地址和端口 -Dcsp.sentinel.api.port=8719 -Dcsp.sentinel.app.type=1
 *
 * sentinel-dashboard docker启动方式：
 * docker run --name sentinel  -d -p 8858:8858 -d  bladex/sentinel-dashboard:1.8.0
 */
@Configuration
public class GatewayConfiguration {

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public JsonSentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        // Register the block exception handler for Spring Cloud Gateway.
        return new JsonSentinelGatewayBlockExceptionHandler();
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    /**
     * 生命周期
     */
    @PostConstruct
    public void doInit() {
        initCustomizedApis();
        initGatewayRules();
        initSystemRule();
    }

    /**
     * 自定义api规则
     * api名称  匹配模式前缀
     */
    private void initCustomizedApis() {
        Set<ApiDefinition> definitions = new HashSet<>();
        ApiDefinition api1 = new ApiDefinition("auth-center")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    // 匹配模式前缀
                    // /auth-center/aa/bb   触发限流
                    add(new ApiPathPredicateItem().setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX).setPattern("/auth-center/**"));
                    // 默认精确匹配 触发限流
                    add(new ApiPathPredicateItem().setPattern("/auth-center/login"));
                }});
        ApiDefinition api2 = new ApiDefinition("system-center")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    add(new ApiPathPredicateItem().setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX).setPattern("/system-center/**"));
                }});
        definitions.add(api1);
        definitions.add(api2);
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }

    /**
     * 网关规则
     * 需要流控可以把注释代码打开，或者在sentinel dashboard限制
     */
    private void initGatewayRules() {
        /*Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(new GatewayFlowRule("auth-center")
                .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
                .setCount(1) //限流阈值
                .setIntervalSec(1) //统计时间窗口，单位是秒，默认是 1 秒
        );
        GatewayRuleManager.loadRules(rules);*/
    }

    /**
     *  系统自适应保护
     *  Load（仅对 Linux/Unix-like 机器生效）：当系统 load1 超过阈值，且系统当前的并发线程数超过系统容量时才会触发系统保护。
     *      系统容量由系统的 maxQps * minRt 计算得出。设定参考值一般是 CPU cores * 2.5。
     * CPU usage（1.5.0+ 版本）：当系统 CPU 使用率超过阈值即触发系统保护（取值范围 0.0-1.0）。
     * RT：当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护，单位是毫秒。
     * 线程数：当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
     * 入口 QPS：当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
     */
    private static void initSystemRule() {
        /*List<SystemRule> rules = new ArrayList<SystemRule>();
        SystemRule rule = new SystemRule();
        // max load is 3
        rule.setHighestSystemLoad(3.0);
        // max cpu usage is 80%
        rule.setHighestCpuUsage(0.8);
        // max avg rt of all request is 10 ms
        rule.setAvgRt(10);
        // max total qps is 20
        rule.setQps(0);
        // max parallel working thread is 10
        rule.setMaxThread(10);
        rules.add(rule);
        SystemRuleManager.loadRules(Collections.singletonList(rule));*/
    }
}
